using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;

namespace OpenWaves.HtmlEncoding
{
    public class FormattingTagsTokenizer : ITokenizer
    {
        class TagPosition
        {
            public int Position { get; set; }
            public string Tag { get; set; }
        }

        private static readonly string[] AllowedTags = new[] { "b", "i", "u", "em", "cite", "ins", "del", "strong" };

        public IEnumerable<HtmlToken> Tokenize(string text)
        {
            var openTagsOccurences = AllowedTags
                    .SelectMany(t => text.FindAll(string.Format(CultureInfo.InvariantCulture, "<{0}>", t), StringComparison.OrdinalIgnoreCase).Select(i => new TagPosition { Position = i, Tag = t }))
                    .OrderBy(t => t.Position)
                    .ToList();

            var closeTagsOccurences = AllowedTags
                .SelectMany(t => text.FindAll(string.Format(CultureInfo.InvariantCulture, "</{0}>", t), StringComparison.OrdinalIgnoreCase).Select(i => new TagPosition { Position = i, Tag = t }))
                .OrderBy(t => t.Position)
                .ToList();

            using (var openEnumerator = openTagsOccurences.GetEnumerator())
            using (var closeEnumerator = closeTagsOccurences.GetEnumerator())
            {
                var openCanMove = openEnumerator.MoveNext();
                var closeCanMove = closeEnumerator.MoveNext();

                var stack = new Stack<TagPosition>();
                var tokens = new List<HtmlToken>();

                while (closeCanMove)
                {
                    if (openCanMove && openEnumerator.Current.Position < closeEnumerator.Current.Position)
                    {
                        stack.Push(openEnumerator.Current);
                        openCanMove = openEnumerator.MoveNext();
                    }
                    else
                    {
                        while (stack.Count > 0 && stack.Peek().Tag != closeEnumerator.Current.Tag)
                        {
                            stack.Pop();
                        }
                        if (stack.Count > 0)
                        {
                            Debug.Assert(stack.Peek().Tag == closeEnumerator.Current.Tag);

                            var tagName = closeEnumerator.Current.Tag;
                            var startIndex = stack.Peek().Position;
                            var endIndex = closeEnumerator.Current.Position + tagName.Length + 3 - 1;
                            tokens.Add(new FormattingToken(startIndex, endIndex, tagName));
                            stack.Pop();
                        }
                        closeCanMove = closeEnumerator.MoveNext();
                    }
                }

                return tokens;
            }

        }
    }
}